// Main code file of eirrepo testing suite.
// Please compile and run if major changes have been done to eirrepo!

#include "StdInc.h"

#include <sdk/MemoryUtils.h>
#include <sdk/MemoryUtils.stream.h>
#include <sdk/DynamicTypeSystem.h>

void Test_DTSConditionalStructing( void )
{
    struct dtsHeapAllocator
    {
        inline void* Allocate( size_t memSize )
        {
            return malloc( memSize );
        }

        inline void Free( void *memPtr, size_t memSize )
        {
            return free( memPtr );
        }
    };

    dtsHeapAllocator _memAlloc;

    typedef DynamicTypeSystem <dtsHeapAllocator, void, dtsDefaultLockProvider, conditionalStructRegistryFlavor> testTypeSystem_t;

    testTypeSystem_t typeSystem;

    // Initialize the typeSystem.
    typeSystem._memAlloc = &_memAlloc;

    // We create two types that have different plugins attached to them, depending on the runtime.
    bool isPluginAvailable = true;

    struct test_type_construct_params
    {
        double take_num;
        bool isTestPluginAvailable;
    };

    struct my_test_type
    {
        inline my_test_type( void *sysPtr, void *cparams )
        {
            test_type_construct_params *params = (test_type_construct_params*)cparams;

            this->num = std::move( params->take_num );
            this->isTestPluginAvailable = std::move( params->isTestPluginAvailable );
        }

        inline my_test_type( const my_test_type& right ) = default;

        inline ~my_test_type( void )
        {
            return;
        }

        double num;
        bool isTestPluginAvailable;
    };

    testTypeSystem_t::typeInfoBase *primTypeInfo = typeSystem.RegisterStructType <my_test_type> ( "test_type", NULL );

    assert( primTypeInfo != NULL );

    // Setup some kind of test plugin.
    struct my_test_plugin
    {
        inline void Initialize( GenericRTTI *rtObj )
        {
            return;
        }

        inline void Shutdown( GenericRTTI *rtObj )
        {
            return;
        }

        int theRealDeal = 42;
    };
    
    struct my_test_plugin_availability : public testTypeSystem_t::conditionalPluginStructInterface
    {
        bool IsPluginAvailableDuringRuntime( testTypeSystem_t::pluginDescriptor desc ) const override
        {
            return isPluginAvailable;
        }

        bool IsPluginAvailableAtObject( const GenericRTTI *rtObj, testTypeSystem_t::pluginDescriptor desc ) const override
        {
            testTypeSystem_t::typeInfoBase *typeInfo = testTypeSystem_t::GetTypeInfoFromTypeStruct( rtObj );

            if ( strcmp( typeInfo->name, "test_type" ) == 0 )
            {
                const my_test_type *theType = (const my_test_type*)testTypeSystem_t::GetConstObjectFromTypeStruct( rtObj );

                return theType->isTestPluginAvailable;
            }

            return false;
        }

        bool isPluginAvailable;
    };

    my_test_plugin_availability tplug_avail;
    tplug_avail.isPluginAvailable = true;

    testTypeSystem_t::pluginOffset_t pluginOff = typeSystem.RegisterDependantConditionalStructPlugin <my_test_plugin> ( primTypeInfo, testTypeSystem_t::ANONYMOUS_PLUGIN_ID, &tplug_avail );

    GenericRTTI *firstObj;
    {
        test_type_construct_params cparams;
        cparams.take_num = 1337.9001;
        cparams.isTestPluginAvailable = tplug_avail.isPluginAvailable;

        firstObj = typeSystem.Construct( NULL, primTypeInfo, &cparams );
    }

    assert( firstObj != NULL );

    // Now without the test plugin.
    tplug_avail.isPluginAvailable = false;

    GenericRTTI *secObj;
    {
        test_type_construct_params cparams;
        cparams.take_num = 0xCAFEBABE;
        cparams.isTestPluginAvailable = tplug_avail.isPluginAvailable;

        secObj = typeSystem.Construct( NULL, primTypeInfo, &cparams );
    }

    // Check the object sizes of both constructed objects.
    size_t firstObjSize = typeSystem.GetTypeStructSize( NULL, firstObj );
    size_t secObjSize = typeSystem.GetTypeStructSize( NULL, secObj );

    assert( firstObjSize == ( sizeof(GenericRTTI) + sizeof(my_test_type) + sizeof(my_test_plugin) ) );
    assert( secObjSize == ( sizeof(GenericRTTI) + sizeof(my_test_type) ) );

    // See if all values got passed properly.
    {
        my_test_type *firstNativeObj = (my_test_type*)testTypeSystem_t::GetObjectFromTypeStruct( firstObj );

        assert( firstNativeObj->num == 1337.9001 );
    }

    typeSystem.Destroy( NULL, secObj );
    typeSystem.Destroy( NULL, firstObj );
}

int main( int argc, char *argv[] )
{
    // TODO: add all kinds of tests here!

    Test_DTSConditionalStructing();

    // Done.
    return 0;
}